using System;
using System.Collections.Generic;
using UnityEngine;

#if FPLIBRARY
using FPLibrary;
using Fix64 = FPLibrary.Fix64;
using FPRandom =FPLibrary.FPRandom; 
using FPVector = FPLibrary.FPVector;
using FPMath = FPLibrary.FPMath;
#else
using Fix64 = System.Single;
using FPRandom = UnityEngine.Random; 
using FPVector = UnityEngine.Vector3;
using FPMath = UnityEngine.Mathf;
#endif


namespace UFE3D
{
    public static class RandomUtils
    {
        private static readonly List<int> s_WeightCache =new List<int>();
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="items"></param>
        /// <param name="weightFunc"></param>
        /// <param name="multiplierToWeight">当权重都很小时(如1/1.1/1.2等),通常保持100,一般权重越小这个应该越大越.如果已经是按100总数来分配权重了,那这个最好就是1.</param>
        /// <param name="debugRandom"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T WeightedRandom<T>(IList<T> items, Func<T, Fix64> weightFunc, Fix64 multiplierToWeight = 100f, bool debugRandom = false)
        {
            if (items.Count == 0 || weightFunc == null)
            {
                return default;
            }

            if (items.Count == 1)
            {
                return items[0];
            }

            s_WeightCache.Clear();

            var i = 0;
            for (i = 0; i < items.Count; i++)
            {
                var weight = Mathf.CeilToInt(weightFunc.Invoke(items[i]) * multiplierToWeight);
                s_WeightCache.Add(Mathf.Max(weight, 1) + (i > 0 ? s_WeightCache[i - 1] : 0));
            }

            var random = FPRandom.Range(0, s_WeightCache[^1]);
    
            for (i = 0; i < s_WeightCache.Count; i++)
            {
                if (s_WeightCache[i] > random)
                {
                    break;
                }
            }


            if (i >= items.Count)
            {
                i = items.Count - 1;
#if UNITY_EDITOR
                Debug.LogError("RandomUtils: WeightedRandom index out of range! items are:");
                for (int j = 0; j < items.Count; j++)
                {
                    Debug.Log($"index:{j}, item:{items[j]}, weight:{weightFunc.Invoke(items[j])}");
                }
#endif
            }
#if UNITY_EDITOR
            else
            {
                if (debugRandom)
                {
                    for (var j = 0; j < s_WeightCache.Count; j++)
                    {
                        Debug.Log($"元素:{items[j]}, 应用区间:{(j == 0 ? 0f : s_WeightCache[j - 1])} - {s_WeightCache[j]}");
                    }
                    
                    Debug.Log($"随机到:{random}, 故结果索引:{i}, 元素:{items[i]}, 权重:{weightFunc.Invoke(items[i])}");
                }
            }
#endif
   
            s_WeightCache.Clear();
  
            return items[i];
        }

        /// <summary>
        /// 检查概率
        /// </summary>
        /// <param name="chance">0-1</param>
        /// <param name="threshold"></param>
        /// <returns></returns>
        public static bool CheckChance(Fix64 chance, Fix64 threshold = 0.000001f)
        {
            if (chance <= threshold)
            {
                return false;
            }
            
            var ran = FPRandom.Range(0f, 1f);
            return ran <= chance;
        }

        public static FPVector RandomInRadiusZX(Fix64 smallRadius, Fix64 bigRadius, bool onlyFrontZ = true)
        {
            // 确保小圆半径小于大圆半径
            if (smallRadius >= bigRadius)
            {
                Debug.LogError("Small radius should be smaller than big radius.");
                return FPVector.zero;
            }

            Fix64 angle = FPRandom.Range(0f, 1f) * 2f *
                          // 随机角度
#if FPLIBRARY
                          FPMath.Pi;
#else
                          FPMath.PI;
#endif
            Fix64 radius = FPMath.Sqrt(FPRandom.Range(smallRadius * smallRadius, bigRadius * bigRadius)); // 随机半径，使用平方根确保分布均匀

            // 极坐标转笛卡尔坐标
            Fix64 x = radius * FPMath.Cos(angle);
            Fix64 z = radius * FPMath.Sin(angle);
            if (onlyFrontZ && z > 0f)
            {
                z *= -1f;
            }

            return new FPVector(x, 0f, z);
        }

        public static Vector3 RandomInRange(Vector3 range)
        {
            var rx = FPRandom.Range(range.x * -1f, range.x);
            var ry = FPRandom.Range(range.y * -1f, range.y);
            var rz = FPRandom.Range(range.z * -1f, range.z);
            return new Vector3(rx, ry, rz);
        }
    }
}